package com.gmadmin.sysadmin.security.controller;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.gmadmin.commoncore.api.R;
import com.gmadmin.commoncore.exception.BaseException;
import com.gmadmin.gmmbg.dto.PermsDto;
import com.gmadmin.sysadmin.annotation.rest.AnonymousDeleteMapping;
import com.gmadmin.sysadmin.annotation.rest.AnonymousGetMapping;
import com.gmadmin.sysadmin.annotation.rest.AnonymousPostMapping;
import com.gmadmin.sysadmin.security.config.CaptchaConfig;
import com.gmadmin.sysadmin.security.config.LoginProperties;
import com.gmadmin.sysadmin.security.config.SecurityProperties;
import com.gmadmin.sysadmin.security.dto.*;
import com.gmadmin.sysadmin.security.enums.LoginCodeEnum;
import com.gmadmin.sysadmin.security.filter.TokenProvider;
import com.gmadmin.sysadmin.security.service.OnlinePermsService;
import com.gmadmin.sysadmin.security.service.OnlineUserService;
import com.gmadmin.sysadmin.security.service.UserDetailsServiceImpl;
import com.gmadmin.sysadmin.security.utils.RedisUtils;
import com.gmadmin.sysadmin.security.utils.SecurityUtils;
import com.wf.captcha.base.Captcha;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 授权、根据token获取用户详细信息
 */
@Slf4j
@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
@Tag(name = "系统：系统授权接口", description = "包含登录相关接口")
public class AuthController {

    private final SecurityProperties properties;
    private final RedisUtils redisUtils;
    private final OnlineUserService onlineUserService;
    private final TokenProvider tokenProvider;
    private final LoginProperties loginProperties;
    private final CaptchaConfig captchaConfig;
    private final PasswordEncoder passwordEncoder;
    private final UserDetailsServiceImpl userDetailsService;
    private final OnlinePermsService onlinePermsService;
    private final SecurityUtils securityUtils;

    @Operation(summary = "登录", description = "登录接口")
    @AnonymousPostMapping(value = "/login")
    public R<LoginDto> login(@Validated @RequestBody AuthUserDto authUser, HttpServletRequest request) throws Exception {
        // 密码解密
        String password = authUser.password();
        // 查询验证码
        String code = redisUtils.get(authUser.uuid(), String.class);
        // 清除验证码
        redisUtils.del(authUser.uuid());
        if (StrUtil.isBlank(code)) {
            throw new BaseException("验证码不存在或已过期");
        }
        if (StrUtil.isBlank(authUser.code()) || !authUser.code().equalsIgnoreCase(code)) {
            throw new BaseException("验证码错误");
        }
        // 获取用户信息
        UserDetailsDto userDetailsDto = userDetailsService.loadUserByUsername(authUser.username());
        // 验证用户密码
        if (!passwordEncoder.matches(password, userDetailsDto.getPassword())) {
            throw new BaseException("登录密码错误");
        }
        // 生成令牌
        String token = tokenProvider.createToken(userDetailsDto);
        // 返回 token 与 用户信息
        LoginDto loginDto = new LoginDto(properties.getTokenStartWith() + token, userDetailsDto);
        if (loginProperties.isSingleLogin()) {
            // 踢掉之前已经登录的token
            onlineUserService.kickOutForUsername(authUser.username());
        }
        // 保存在线信息
        String loginKey = tokenProvider.loginKey(token);
        onlineUserService.save(loginKey, userDetailsDto, token, request);
        // 返回登录信息
        return R.ok(loginDto);
    }

    @Operation(summary = "获取用户信息", description = "查询用户信息")
    @GetMapping(value = "/info")
    public R<OnlineUserDto> getUserInfo() {
        return R.ok(securityUtils.getCurrentUserCache());
    }

    @Operation(summary = "查询当前用户权限", description = "查询当前用户权限")
    @GetMapping(value = "/currentPerms")
    public R<List<PermsDto>> findCurrentPerms(){
        OnlineUserDto currentUserCache = securityUtils.getCurrentUserCache();
        List<PermsDto> perms = new ArrayList<>();
        for (String role : currentUserCache.userDetailsDto().getRoles()) {
            List<PermsDto> permsDto = onlinePermsService.getPermsDto(role);
            perms.addAll(permsDto);
        }
        return R.ok(perms);
    }

    @Operation(summary = "刷新权限缓存", description = "刷新权限缓存")
    @GetMapping(value = "/reflushPerms")
    public R<String> reflushPerms(){
        onlinePermsService.refresh();
        return R.ok("刷新成功！");
    }

    @Operation(summary = "测试接口", description = "测试接口")
    @GetMapping(value = "/hello")
    public R<String> hello(){
        return R.ok("hello");
    }

    @Operation(summary = "获取验证码", description = "查询验证码")
    @AnonymousGetMapping(value = "/code")
    public R<CodeDto> getCode() {
        // 获取运算的结果
        Captcha captcha = captchaConfig.getCaptcha();
        String uuid = IdUtil.simpleUUID();
        //当验证码类型为 arithmetic时且长度 >= 2 时，captcha.text()的结果有几率为浮点型
        String captchaValue = captcha.text();
        if (captcha.getCharType() - 1 == LoginCodeEnum.ARITHMETIC.ordinal() && captchaValue.contains(".")) {
            captchaValue = captchaValue.split("\\.")[0];
        }
        // 保存
        redisUtils.set(uuid, captchaValue, captchaConfig.getExpiration(), TimeUnit.MINUTES);
        // 验证码信息
        return R.ok(new CodeDto(captcha.toBase64(), uuid));
    }

    @Operation(summary = "退出登录", description = "退出登录接口")
    @AnonymousDeleteMapping(value = "/logout")
    public R<String> logout(HttpServletRequest request) {
        String loginKey = tokenProvider.loginKey(tokenProvider.getToken(request));
        onlineUserService.logout(loginKey);
        return R.ok("退出登录成功");
    }

}
